/* @file   DynamicKeypad.pde
|| @version 1.0
|| @author Mark Stanley
|| @contact mstanley@technologist.com
||
|| @dificulty:  Intermediate
||
||  *** THE KEYPAD REQUIRES PULL-UP RESISTORS ON THE ROW PINS. ***
||
|| @description
|| |    This is a demonstration of keypadEvents. It's used to switch between keymaps
|| |    while using only one keypad.  The main concepts being demonstrated are:
|| |
|| |        Using the keypad events, PRESSED, HOLD and RELEASED to simplify coding.
|| |        How to use setHoldTime() and why.
|| |        Making more than one thing happen with the same key.
|| |        Assigning and changing keymaps on the fly.
|| |
|| |    Another useful feature is also included with this demonstration although
|| |    it's not really one of the concepts that I wanted to show you.  If you look
|| |    at the code in the PRESSED event you will see that the first section of that
|| |    code is used to scroll through three different letters on each key.  For 
|| |    example, pressing the '2' key will step through the letters 'd', 'e' and 'f'.
|| |
|| |
|| |  Using the keypad events, PRESSED, HOLD and RELEASED to simplify coding
|| |    Very simply, the PRESSED event occurs imediately upon detecting a pressed
|| |    key and will not happen again until after a RELEASED event.  When the HOLD
|| |    event fires it always falls between PRESSED and RELEASED.  However, it will
|| |    only occur if a key has been pressed for longer than the setHoldTime() interval.
|| |
|| |  How to use setHoldTime() and why
|| |    Take a look at keypad.setHoldTime(500) in the code.  It is used to set the
|| |    time delay between a PRESSED event and the start of a HOLD event.  The value
|| |    500 is in milliseconds (mS) and is equivalent to half a second.  After pressing
|| |    a key for 500mS the HOLD event will fire and any code contained therein will be
|| |    executed.  This event will stay active for as long as you hold the key except
|| |    in the case of bug #1 listed above.
|| |
|| |  Making more than one thing happen with the same key.
|| |    If you look under the PRESSED event (case PRESSED:) you will see that the '#'
|| |    is used to print a new line, Serial.println().  But take a look at the first
|| |    half of the HOLD event and you will see the same key being used to switch back
|| |    and forth between the letter and number keymaps that were created with alphaKeys[4][5]
|| |    and numberKeys[4][5] respectively.
|| |
|| |  Assigning and changing keymaps on the fly
|| |    You will see that the '#' key has been designated to perform two different functions
|| |    depending on how long you hold it down.  If you press the '#' key for less than the
|| |    setHoldTime() then it will print a new line.  However, if you hold if for longer
|| |    than that it will switch back and forth between numbers and letters.  You can see the
|| |    keymap changes in the HOLD event.
|| |
|| |  
|| |  In addition...
|| |      You might notice a couple of things that you won't find in the Arduino language
|| |    reference.  The first would be #include <ctype.h>.  This is a standard library from
|| |    the C programming language and though I don't normally demonstrate these types of 
|| |    things from outside the Arduino language reference I felt that its use here was 
|| |    justified by the simplicity that it brings to this sketch.  
|| |      That simplicity is provided by the two calls to isalpha(key) and isdigit(key).  
|| |    The first one is used to decide if the key that was pressed is any letter from a-z
|| |    or A-Z and the second one decides if the key is any number from 0-9.  The return 
|| |    value from these two functions is either a zero or some positive number greater 
|| |    than zero.  This makes it very simple to test a key and see if it is a number or 
|| |    a letter.  So when you see the following:
|| |
|| |    if (isalpha(key))    // this tests to see if your key was a letter
|| |
|| |    And the following may be more familiar to some but it is equivalent:
|| |
|| |    if (isalpha(key) != 0)   // this tests to see if your key was a letter
|| |
|| |  And Finally...
|| |    To better understand how the event handler affects your code you will need to remember
|| |    that it gets called only when you press, hold or release a key.  However, once a key
|| |    is pressed or held then the event handler gets called at the full speed of the loop().
|| |
|| |  *** THE KEYPAD REQUIRES PULL-UP RESISTORS ON THE ROW PINS. ***
|| #
*/
#include <Keypad.h>
#include <ctype.h>

// Define the keymaps.  The blank spot (lower left) is the space character.
char alphaKeys[4][3] = {
    { 'a','d','g' },
    { 'j','m','p' },
    { 's','v','y' },
    { ' ','.','#' }
};

char numberKeys[4][3] = {
    { '1','2','3' },
    { '4','5','6' },
    { '7','8','9' },
    { ' ','0','#' }
};

boolean alpha = false;   // Start with the numeric keypad.

char* keypadMap = (alpha == true) ? makeKeymap(alphaKeys) : makeKeymap(numberKeys);

// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these pins, eg. ROW0 = Arduino pin2.
byte rowPins[] = { 9, 8, 7, 6 };

// Connect keypad COL0, COL1 and COL2 to these pins, eg. COL0 = Arduino pin6.
byte colPins[] = { 12, 11, 10 };

//create a new Keypad
Keypad keypad = Keypad(keypadMap, rowPins, colPins, sizeof(rowPins), sizeof(colPins));

const byte ledPin = 13;	                                                 // Use the LED on pin 13.

void setup() {
    Serial.begin(9600);
    digitalWrite(ledPin, HIGH);                                                // Turns the LED on.
    keypad.addEventListener(keypadEvent);                                      // Add an event listener.
    keypad.setHoldTime(500);                                                   // Default is 1000mS
    keypad.setDebounceTime(250);                                               // Default is 50mS
}

void loop() {
    char key = keypad.getKey();

    if (alpha) {                      // Flash the LED if we are using the letter keymap.
        digitalWrite(ledPin,!digitalRead(ledPin));
        delay(100);
    }
}

// Take care of some special events.
void keypadEvent(KeypadEvent key) {
    static char virtKey = NO_KEY;      // Stores the last virtual key press. (Alpha keys only)
    static char physKey = NO_KEY;      // Stores the last physical key press. (Alpha keys only)
    static char buildStr[12];
    static byte buildCount;
    static byte pressCount;

    switch (keypad.getState())
    {
    case PRESSED:
        if (isalpha(key)) {              // This is a letter key so we're using the letter keymap.
            if (physKey != key) {        // New key so start with the first of 3 characters.
                pressCount = 0;
                virtKey = key;
                physKey = key;
            }
            else {                       // Pressed the same key again...
                virtKey++;               // so select the next character on that key.
                pressCount++;            // Tracks how many times we press the same key.
            }
            if (pressCount > 2) {        // Last character reached so cycle back to start.
                pressCount = 0;
                virtKey = key;
            }
            Serial.print(virtKey);       // Used for testing.
        }
        if (isdigit(key) || key == ' ' || key == '.')  Serial.print(key);
        if (key == '#')  Serial.println();
        break;

    case HOLD:
        if (key == '#')  {                   // Toggle between keymaps.
            if (alpha == true)  {            // We are currently using a keymap with letters
                keypad.begin(*numberKeys);   // and want to change to numbers.
                alpha = false;
            }
            else  {                          // Or, we are currently using a keymap with numbers
                keypad.begin(*alphaKeys);    // and want to change to letters.
                alpha = true;
            }
        }
        else  {                             // Some key other than '#' was pressed.
            buildStr[buildCount++] = (isalpha(key)) ? virtKey : key;
            buildStr[buildCount] = '\0';
            Serial.println();
            Serial.println(buildStr);
        }
        break;

    case RELEASED:
        if (buildCount >= sizeof(buildStr))  buildCount = 0;    // Our string is full. Start fresh.
        break;

    }  // end switch-case
}  // end keypad events
